package cn.ztydata.teachingsystem.heaven.service;

import cn.ztydata.teachingsystem.common.Page;
import cn.ztydata.teachingsystem.exception.ServiceException;
import cn.ztydata.teachingsystem.heaven.common.WebContext;
import cn.ztydata.teachingsystem.heaven.cons.Constants;
import cn.ztydata.teachingsystem.heaven.cons.Roles;
import cn.ztydata.teachingsystem.heaven.dao.*;
import cn.ztydata.teachingsystem.heaven.entity.Department;
import cn.ztydata.teachingsystem.heaven.entity.SpecStatistics;
import cn.ztydata.teachingsystem.heaven.entity.Specialty;
import cn.ztydata.teachingsystem.heaven.entity.User;
import cn.ztydata.teachingsystem.heaven.exception.ExcelException;
import cn.ztydata.teachingsystem.util.ExcelUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 专业服务类
 *
 * @author fanruiping
 * @since 2014-11-29
 */
@Service
public class SpecialtyService extends BaseService {
    @Autowired
    private SpecialtyDao specialtyDao;
    @Autowired
    private DepartmentDao departmentDao;
    @Autowired
    private CourseService courseService;
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private ResourceDao resourceDao;
    @Autowired
    private CourseDao courseDao;

    @Autowired
    private WebContext webContext;

    /**
     * 获取指定系部下的专业数据列表
     * @param deptNum 系部编码
     * @param pageNum 页码
     * @param pageSize 每页显示条数
     * @return Page
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public Page<Specialty> getList(User userInfo,String deptNum, int pageNum, int pageSize){
        List<String> deptNums = new ArrayList<>();

        if("ALL".equals(deptNum) && userInfo.getRole() == Roles.DEPT_ADMIN){
            log.debug("系部编码为ALL且用户角色为系部管理员:\"ALL\".equals(deptNum) && userInfo.getRole() == Roles.DEPT_ADMIN");

            Set<Long> deptIds = roleDao.getByTeacherId(userInfo.getUserId(), Roles.SPEC_LEADER);

            List<Department> departments = departmentService.getByIds(deptIds);

            for(Department entity : departments){
                log.debug("循环系部列表获取系部编码:Department entity : departments");

                deptNums.add(entity.getNumber());
            }
        }else if(!"ALL".equals(deptNum)){
            log.debug("系部编码不等于ALL:!\"ALL\".equals(deptNum)");

            deptNums.add(deptNum);
        }

        Page<Specialty> page = specialtyDao.getList(deptNums, pageNum, pageSize);

        List<String> numbers = new ArrayList<>();

        for(Specialty entity : page.getItems()){
            log.debug("循环专业列表数据获取系部编码");

            numbers.add(entity.getDeptNum());
        }

        Map<String,String> departments = departmentDao.getNamesByNumbers(numbers);

        //循环设置系部名称
        if(!departments.isEmpty()){
            log.debug("系部数据集合不为空:!departments.isEmpty()");

            for(Specialty entity : page.getItems()){
                log.debug("循环专业列表插入系部名称");

                entity.setDeptName(departments.get(entity.getDeptNum()));
            }
        }

        log.debug("return specialty list");

        return page;
    }

    /**
     * 获取指定专业详细数据
     * @param id 编号
     * @return Specialty
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public Specialty getDetail(long id){
        log.debug("return specialty detail by id");

        return specialtyDao.getById(id);
    }

    /**
     * 根据专业编码获取详细数据
     * @param number 专业编码
     * @return Specialty
     *
     * @author fanruiping
     * @since 2014-12-01
     */
    public Specialty getDetailByNumber(String number){
        log.debug("return specialty detail by number");

        return specialtyDao.getByNumber(number);
    }

    /**
     * 根据专业编码和编号获取详细数据
     * @param number 专业编码
     * @param id 专业编号
     * @return Specialty
     *
     * @author fanruiping
     * @since 2014-12-01
     */
    public Specialty getDetailByNumberAndId(String number, long id){
        log.debug("return specialty detail by number and id");

        return specialtyDao.getByNumberAndId(number, id);
    }
    /**
     * 新增专业数据
     * @param entity 实体类
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public void add(Specialty entity){
        specialtyDao.add(entity);
    }

    /**
     * 编辑专业数据
     * @param entity 实体类
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public void modify(Specialty entity){
        //更新课程所属专业名称
        if(-1 == courseDao.updateSpecialtyName(entity.getId(),entity.getName())){
            log.debug("update course specialtyName fail");

            log.warn("更新课程所属专业名称失败");

            throw new ServiceException("更新课程所属专业名称失败");
        }

        //更新资源所属专业名称
        if(-1 == resourceDao.updateSpecialtyName(entity.getId(),entity.getName())){
            log.debug("update resource specialtyName fail");

            log.warn("更新资源所属专业名称失败");

            throw new ServiceException("更新资源所属专业名称失败");
        }

        if(!specialtyDao.modify(entity)){
            log.debug("update specialty fail");

            log.error("更新资源数据失败");

            throw new ServiceException("更新资源数据失败");
        }
    }

    /**
     * 删除指定专业数据
     * @param id 编号
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public int delete(long id){
        log.debug("delete specialty");

        return specialtyDao.delete(id);
    }

    /**
     * 验证系部下是否有专业
     * @param deptNum 系部编码
     * @return Boolean
     *
     * @author fanruiping
     * @since 2014-11-29
     */
    public Boolean checkHasSpecialtyByDeptNum(String deptNum){
        log.debug("判断系部下是否有专业数据");

        return specialtyDao.checkByDeptNum(deptNum);
    }

    /**
     * 获取系部下的所有专业数据列表
     * @param deptNum 系部编码
     * @param tag 标记（0：全部，1：按照角色获取）
     * @return List
     *
     * @author fanruiping
     * @since 2014-12-01
     */
    public List<Specialty> getByDeptNum(String deptNum, int tag){
        log.debug("return specialty by deptNum");

        Long userId = 1 == tag && Roles.SPEC_LEADER == webContext.getRole() ? webContext.getUserId() : 0;

        return specialtyDao.getByDeptNumAndLeader(deptNum,userId);
    }

    /**
     * 获取所有专业数据
     * @return List
     *
     * @author fanruiping
     * @since 2014-12-02
     */
    public List<Specialty> getAll(){
        log.debug("返回所有系部数据");

        return specialtyDao.getAll();
    }

    /**
     * 读取Excel文件
     * @param in 文件输入流
     *
     * @author chenxin
     * @since 2014/11/29
     */
    public List<Specialty> readFromExcel(InputStream in) throws IOException {
        //定义标题-属性的映射
        LinkedHashMap<String, String> fieldMap = Maps.newLinkedHashMap();
        fieldMap.put("专业编码", "number");
        fieldMap.put("专业名称", "name");
        fieldMap.put("系部编码", "deptNum");
        fieldMap.put("培养目标", "trainingObjective");
        fieldMap.put("就业方向", "occupationalDirection");
        fieldMap.put("职业资格证", "certificate");
        fieldMap.put("专业简介", "intro");

        log.debug("转换Excel专业信息为List");
        return ExcelUtil.excelToList(in, Specialty.class, fieldMap);
    }

    /**
     * 批量新增专业
     * @param specialtyList 系部集合
     *
     * @author cx
     * @since 2014-11-29
     */
    @Transactional
    public void addSpecs(final List<Specialty> specialtyList) {
        try {
            //执行批量插入
            specialtyDao.saveSpecs(specialtyList);
        } catch(DataAccessException ex) {
            log.debug("导入专业-批量新增异常");

            log.warn("导入专业-批量新增异常", ex);
            throw new ExcelException();
        }
    }

    /**
     * 按照系部统计专业总数
     * @return map
     *
     * @author cx
     * @since 2014-12-01
     */
    public Map<String, Long> getSpecTotalForDept() {
        //获取系部的专业总数
        List<Map<String, Object>> specialtyTotalList = specialtyDao.getTotalForDept();
        //将统计信息转换为“系部ID->专业总量”格式
        Map<String, Long> deptToSpecialty = Maps.newHashMap();
        log.debug("循环转换专业统计信息格式");
        for (Map<String, Object> specialtyTotal : specialtyTotalList) {
            deptToSpecialty.put((String) specialtyTotal.get("deptNum"), (Long) specialtyTotal.get("total"));
        }

        log.debug("return 专业统计信息map");
        return deptToSpecialty;
    }

    /**
     * 统计专业信息
     * @param deptNum 系部编码
     * @return List
     *
     * @author cx
     * @since 2014-12-02
     */
    @Cacheable(value = "teachingSystem:cache:count", key = "'spec:'.concat(#deptNum)")
    public List<SpecStatistics> getStatisticsInfo(final String deptNum) {
        //获取指定系部的ID
        Department department = departmentDao.getByNumber(deptNum);
        if (department == null) {
            log.debug("不存在的系部相关信息:department == null");
            throw new ServiceException("不存在的系部相关信息");
        }

        //获取专业的ID和名称
        List<SpecStatistics> specInfoList = specialtyDao.getListByDeptNum(deptNum);
        if (specInfoList.isEmpty()) {
            log.debug("暂无专业统计信息:specInfoList.isEmpty()");
            throw new ServiceException("暂无专业统计信息");
        }

        //统计专业下的课程数量
        Map<Long, Long> specToCourse = courseService.getCourseTotalForSpec(department.getId());

        //统计专业下的基本资源数量
        Map<Long, Long> specToRsc = resourceService.countForSpec(department.getId());

        log.debug("循环专业统计的信息");
        //组合专业资源的统计信息
        for (SpecStatistics specStatistics : specInfoList) {
            //获取课程数量、资源数量
            Long courseTotal = specToCourse.get(specStatistics.getId());
            Long rscTotal = specToRsc.get(specStatistics.getId());

            specStatistics.setCourseTotal(courseTotal == null ? 0 : courseTotal);
            specStatistics.setRscTotal(rscTotal == null ? 0 : rscTotal);
        }

        log.debug("return 专业统计信息集合");
        return specInfoList;
    }

    /**
     * 检查导入专业数据中的编码冲突
     * @param specialtyList 专业集合
     *
     * @author cx
     * @since 2014-12-04
     */
    public void checkForNum(final List<Specialty> specialtyList) {
        if (specialtyList.size() > Constants.TOTAL_2K) {
            log.debug("导入数据量超过" + Constants.TOTAL_2K);
            throw new ExcelException("导入数据量超过" + Constants.TOTAL_2K);
        }

        //抽取专业的编码集合
        Set<String> specNumSet = Sets.newHashSet();
        int index = 1;
        log.debug("循环校验导入专业信息的编码冲突");
        for (Specialty specialty : specialtyList) {
            if (specialty.getNumber() == null) {
                log.debug("导入了编码为null的专业:specialty.getNumber() == null");
                throw new ExcelException("导入数据中第" + index + "条数据专业编码为空");
            }

            //检查导入数据中是否存在重复编码
            if (specNumSet.contains(specialty.getNumber())) {
                log.debug("导入专业中存在重复编码:specNumSet.contains(specialty.getNumber())");
                throw new ExcelException("导入数据中第" + index + "条数据专业编码重复" );
            }

            specNumSet.add(specialty.getNumber());
            index++;
        }

        //检查导入数据编码是否与数据库中记录冲突
        String duplicateNum = specialtyDao.getDuplicateNum(specNumSet);
        specNumSet.clear();
        if (duplicateNum != null) {
            log.debug("导入的专业编码与表记录冲突:duplicateNum != null");
            throw new ExcelException("编码为:" + duplicateNum + "的专业已经存在");
        }
    }

    /**
     * 获取专业负责人负责的专业集合
     * @param leaderId 专业负责人编号
     * @return List
     *
     * @author fanruiping
     * @since 2014-12-10
     */
    public List<Specialty> getByLeaderId(long leaderId){
        log.debug("return specialty list by leaderId");

        return specialtyDao.getByLeaderId(leaderId);
    }

    /**
     * 获取专业负责人负责的专业集合
     * @param currentId 当前登录用户编号
     * @return List
     *
     * @author fanruiping
     * @since 2014-12-10
     */
    public Set<Long> statisticsRscBySpecialty(String type,long currentId){
        log.debug("return specialty list by leaderId");

        Set<Long> ids = new HashSet<>();

        List<Specialty> specialties = specialtyDao.getByLeaderId(currentId);

        for(Specialty specialty : specialties){
            ids.add(specialty.getId());
        }

        return ids;
    }

    /**
     * 获取系部管理员管理的专业
     *
     * @author chenxin
     * @since 2015/1/14
     */
    public List<Specialty> getForDeptLeader(final long deptLeaderId) {
        log.debug("return spec list for deptLeaderId");

        return specialtyDao.getAllForDeptLeader(deptLeaderId);
    }

    /**
     * 判断用户是否是指定专业的专业负责人
     * @param userId 用户编号
     * @param specialtyId 专业编号
     * @return Boolea
     *
     * @author fanruiping
     * @since 2014-12-24
     */
    public Boolean checkUserIsLeader(Long userId,Long specialtyId){
        log.debug("check user is specialty leader");

        return specialtyDao.checkLeader(userId,specialtyId);
    }

    /**
     * 获取首页专业列表信息
     *
     * @author chenxin
     * @since 2015/1/10
     */
    public List<Specialty> getIndexList(final String deptNum, final int top) {
        //获取指定系部的前n个专业
        log.debug("获取指定系部的前n个专业");
        return specialtyDao.getTopForDept(deptNum, top);
    }


}
